# coding: utf8

from pprint import pprint, pformat
from gluon.contrib.markdown.markdown2 import markdown  #~ http://code.google.com/p/python-markdown2/wiki/Extras

data__Locales = prefetch_db_table_to_dict("Locales")
#~ data__Languages = prefetch_db_table_to_dict("Languages")

user_prefs = session.user_prefs =  session.get('user_prefs', Storage())  # init user_prefs variable
cache_invalidate = session.cache_invalidate =  session.get('cache_invalidate', Storage())   # a workaround to reset caching (esp for GAE)

# init with defaults if needed
# for topic_examples
example_parts2toggle = 'explanation full_code nonessential_surrounding_code output meta_info'.split()
example_parts_human = dict([ ( key, T(' '.join( key.split('_') ).title()) ) for key in example_parts2toggle ] ) # prepair example parts for translation
user_prefs_init('shown_example_parts', default_value= set(example_parts2toggle[:-1]) ) # don't show meta_info by default
# for topic_examples and example_part_view ajaxed
user_prefs_init( 'shown_languages', default_value = set(['Python', 'Java']) )  # if var (shown_languages) not present, init with default_value

if request.vars.current_root_id :
    try:    
        current_root_id = session.current_root_id = int(request.vars.current_root_id)
    except Exception, e:
        response.flash = str(e) 

current_root_id = session.current_root_id = session.get('current_root_id', find_root_id() )


data__Languages = prefetch_db_table_to_dict('Languages')
if request.get_vars.langs: #override  shown_languages
    forced_Languages = []
    for lang_ext in request.get_vars.langs.split(','):
        # search for extension
        for id, row in data__Languages.items():
            if row.extension  == lang_ext:
                forced_Languages.append( row.name )
                
    if forced_Languages:
        user_prefs['shown_languages'] = set( forced_Languages )

user_prefs_init( 'locale', default_value = 'lt-lt' )
#~ locales = {'lt-lt': "Lietuvių", 'en-en': "English"}
#~ locales = [ row.locale for row in db(db.Locales>0).select() ]

#~ if auth.has_membership('admin'):
#~ if auth.has_permission('update', 'Topics'):
    #~ caching_time = 0



user_prefs_init('use_caching', 'true')
if user_prefs_get('use_caching') == 'false': # here are javascript 'true' and 'false'
    caching_time = 0

user_prefs.caching_time = caching_time # TODO: make it configurable by user??

session.has_permission_update_Topics = auth.has_permission('update', 'Topics')

def cache_isneeded_by_url():
    return request.function in 'topic_examples index'.split() \
    or request.function.startswith('example_view')
    
def cache_track_key(key): # for GAE mainly
    #~ if cache_isneeded_by_url:
        if db(db.Cache_keys.key_ == key).count()==0:
            db.Cache_keys.insert(key_=key, url=request.env.path_info)


def cache_invalidate_4topics_tree():
    for locale in data__Locales.values():
        for topic_editor in (True, False):
            key = cache_key_4topics_tree(locale=locale.locale, topic_editor=str(topic_editor) )
            #~ response.flash = (" cache_invalidate_4topics_tree %s  %s: " %  (locale, topic_editor)) + key
            cache_invalidate[key] = True

                
    
def cache_key_4topics_tree(content_type='topics_tree', locale=None, topic_editor=None): # it is LOADED from function topic_examples
    """
    should be accesible from topic_examples as well
    generates caching key,
    taking into account:
            view or edit,
            locale,
            ability to update Topics (which is enough to be kind of editor)
    """

    #~ if not cache_isneeded_by_url(): return request.env.path_info
    # a non reliable way to optimize a bit -- bugprone..
    if not "topic" in request.function: return "NO-KEY-NEEDED-HERE: " + request.env.path_info


    locale = locale or user_prefs_get('locale')
    content_type = content_type  or request.function
    if topic_editor==None :
        topic_editor = str(auth.has_permission('update', 'Topics')) 
    
    key = '--'.join( (
        content_type,
        'root_id', str(session.current_root_id),
        #~ 'edit' if request.env.path_info.split('/')[-1]=='edit' else 'view', # this is needed to trick default/index to be the same as /view
        'locale', locale,
        'editor', str(topic_editor)
    ) )
    
    #~ cache_track_key(key)
    return key
    #~ return latinize(key)

def cache_key_4topic(id=None, content_type='topic_', locale=None):  # TODO: don't know how feedback should be refreshed..
    """
    takes into account:
            controller/id/action,
            locale,
            ability to update Topics (which is enough to be kind of editor)
    """
    #~ if not cache_isneeded_by_url(): return request.env.path_info
    # a non reliable way to optimize a bit -- bugprone..    
    if not request.function.startswith('topic_'): return "NO-KEY-NEEDED-HERE: " + request.env.path_info
    

    id = id or request.args(0)
    topic = db.Topics[id]
    locale = locale or user_prefs_get('locale')
    content_type = content_type  or request.function

    key = '--'.join( (
        content_type, str(id), str(topic.name) if topic else ''
        #~ '/'.join( request.env.path_info.split('/')[-3:] ),
        'locale', locale,
        #~ 'auth', str(auth.has_permission('update', 'Topics')),
        ) )
    #~ cache_track_key(key)
    return latinize(key)



    
    
if not user_prefs.locale.startswith('en'):
    T.force(user_prefs.locale) 
session.translation_language = user_prefs.locale[:2]
#~ session.translation_language = 'lt'  # for code explanations/topic_names etc... 
#~ session.translation_language = 'en'  # for code explanations/topic_names etc... 

# # uncomment the following if you have defined "auth" and "crud" in models
def user():
    if request.ajax:
        result = auth()
        if not auth.user:
            return "ERROR" +"<br> " + \
                    T("You should %s first",
                        A('login',  _href=auth.settings.login_url).xml()
                     ) + " <br>\n"
        if isinstance(result, str):
            response.flash = result
            return "ERROR"
        return result # no dict(form=result)
    else:   
        return dict(form=auth())

#~ @auth.requires_login()
#~ def data():   return dict(form=crud())
# def download(): return response.download(request,db)
# # tip: use @auth.requires_login, requires_membership, requires_permission

def get_topic_name_translation(topic, lang=''):
    lang = lang or session.translation_language
    if lang:
        if topic.name_translations:
            for translation in topic.name_translations.split('\n'):
                if translation.strip().lower().startswith(lang.lower()+":"):
                    return translation.strip()[len(lang)+1: ]
    return topic.name #if no luch with finding translation

def index():
      # TODO: move to user? config, or make widget to select
    #~ return dict()
    #~ return topics_tree_view_uncached()
    #~ return dict( topics_tree_view=XML(topics_tree_view()) )
    #~ return dict( Topics = LOAD(URL(r=request, c='default', f='topics_tree_view.load'), ajax=True) )
    #~ '/CodeByExample/default/topics_tree_view.load',
    if db(db.Topics.id > 0).count() == 0 \
    or db(db.Languages.id > 0).count() == 0:
        result = A(
                    T("You need to init data first (Topics and/or Languages)"),
                    _href=URL(r=request, c='admin', f='init_data')
                  )
                 
    else: 
            #~ title=T("Topics"),
            #~ topics_tree = LOAD( c=request.controller, f='topics_tree_view.html', ajax=True )
            response.subtitle = T("Select your starting/root topic")

            def TOC_item_4current_root(topic):
                return A( get_topic_name_translation(topic), _href=URL(f='topic_examples' ,
                                                    args=[topic.id],
                                                    vars=dict(current_root_id=topic.id)
                                                    )
                            )

            TOC = []
            rows__Topics = prefetch_db_table_to_selected_rows('Topics')
            #~ root_topics = find_root_topics() #.as_list()
            root_topics = rows__Topics.find(is_topic_root) #.as_list()
            for root_topic in root_topics:
                TOC.append( 
                    #~ LOAD( c=request.controller,
                       #~ f='topics_tree_view.html'+"?current_root_id=%d"%root_topic.id, # reset current_root_id
                       #~ ajax=True, ajax_trap=False,
                     #~ )
                     LI(
                        TOC_item_4current_root(root_topic) ,
                        UL( [ TOC_item_4current_root( t )  for t in rows__Topics if t.parent_id == root_topic.id ] ) # first level subtopics
                    ),
                )
            
            result =  DIV(
                        # msg 
                         
                        # Topics
                        #~ LOAD( c=request.controller,
                           #~ f='topics_tree_view.html'+"?current_root_id=%d"%find_root_id(), # reset current_root_id
                           #~ ajax=True
                         #~ ),
                         UL(TOC),
                         
                         # Topics_root_style 
                         XML("<style>.choose_root {display: inline} </style>"),
            ) 
    #~ return dict( title=T('Topics Tree'), result=result )
    return response.render( 'default/generic_content_wokeys.html', result=result )


def topics_tree_dragabble_togglable():
    #~ return  DIV( B("labas"), SCRIPT("alert('hi');"), ) # without DIV or similar, won't work
    pos_topics_menu = user_prefs_get('pos_topics_menu_container2drag', default_value=([0,0]) )    
    return DIV(
                            DIV(

                                TABLE( TR(

                                                  TOGGLE_anywhere_by_user_pref('#topics_menu', 'display_topics_menu',
                                                        msg=T('Topics Menu') + T("Toggle"), init_val="false"
                                                  ),

                                            DIV( T('(drag me)'), _class="drag_me", _id="topics_menu_drag_me",),
                                            DIV(
                                                   
                                                    SPAN('[x]',
                                                    _onclick="""ajax('%(url)s'+'/pos_topics_menu_container2drag/0/0', [], 'ajax_dbg');
                                                                $('#topics_menu_container2drag').css({ position: 'relative', left: 0, top: 0 });
                                                                $('#topics_menu_container2drag').draggable('destroy');
                                                                $('#topics_menu_drag_me').hide();
                                                                $(this).hide();
                                                             """ % {'url': url_user_prefs }
                                                             #~ """ % {'url': URL(r=request, f='ajax_update_user_prefs' ) }
                                                    ),
                                                    _style="text-align: right; display:none; margin:0; cursor: pointer",
                                                    _id = "menu_topics_draggable_close"
                                              ),
                                        ), _style="width:105%; margin: 0"),
                                    
                                    DIV(
                                        #~ LOAD(c=request.controller, f='topics_tree_view', ajax=False, ajax_trap=False, target="topics_menu_LOADED"),  # ajax=False as otherwise clicking would do nonsenses :)
                                        LOAD(c=request.controller, f='topics_tree_view', ajax=True, target="topics_menu_LOADED"),

                                          _id="topics_menu"  ,
                                          style="display:%s; " %  user_prefs_get('display_topics_menu', default_value='none'), # hidden by default
                                    ),
                                   
                            _id="topics_menu_container2drag", #_style="margin:0; padding:0"
                            
                            ),
                            # http://stackoverflow.com/questions/1328249/remembering-a-jquery-draggable-tool-palette-position-between-page-refreshes/1328311#1328311
                            SCRIPT("""$(document).ready(function() {

                                    // $.pop();
                                    
                                    //$("#topics_menu_container2drag") .css({ top: $.cookie("paletteY")*1, left: $.cookie("paletteX")*1 }).draggable({ stop: function (event, ui) { $.cookie("paletteX", ui.position.left); $.cookie("paletteY", ui.position.top); } });
                                    //http://www.west-wind.com/Weblog/posts/517320.aspx

                                    var el_topics_menu = $("#topics_menu_container2drag");
                                    function apply_borders_visibility(){
                                        $('#menu_topics_draggable_close').show(); 
                                        el_topics_menu.css({
                                             //position: 'absolute',
                                             border: '1px solid silver',
                                             //opacity: '0.8',
                                             background: '#fff',
                                             //background: 'rgba(255, 255, 255, 0.6)',
                                             //filter: 'progid:DXImageTransform.Microsoft.Gradient(GradientType=0, StartColorStr="#7FFFFFFF", EndColorStr="#7FFFFFFF")',
                                             
                                        });
                                       // el_topics_menu.backOpacity({ background: '#ffffff', opacity: '0.6' });
                                                        
                                    }

                                    var before_initial_drag;                                                                       
                                    var X = %(X)s*1;
                                    var Y = %(Y)s*1;
                                    if (X*Y) {
                                             apply_borders_visibility();  
                                             el_topics_menu .css({ left: X, top: Y ,
                                                                    //marginLeft: 0, marginTop: 0,
                                                                     position:'absolute'
                                                                });
                                            before_initial_drag = false;
                                              
                                    }
                                    else { before_initial_drag = true };
                                    
                                    el_topics_menu .draggable({  cursor: 'crosshair',  appendTo: 'body', // zIndex: 2700,
                                            start: function(event, ui) {

                                                        apply_borders_visibility();
                                                        
                                                        
                                                        
                                                },
                                            stop: function (event, ui) {
                                                        //$.cookie("paletteX", ui.position.left); $.cookie("paletteY", ui.position.top);

                                                        
                                                        var abs_x, abs_y;
                                                        if (before_initial_drag ){  
                                                            
                                                            abs_x = ui.offset.left-20;
                                                            abs_y = ui.offset.top-20;
                                                            el_topics_menu.css({position: 'absolute'});
                                                            el_topics_menu .css({ left: abs_x, top: abs_y } );

                                                            before_initial_drag = false; // set it as done 
                                                            
                                                        }
                                                        else {
                                                            abs_x = ui.position.left;
                                                            abs_y = ui.position.top;
                                                        }
                                                        ajax('%(url)s'+'/pos_topics_menu_container2drag/'+abs_x+'/'+ abs_y, [], 'ajax_dbg');

                                                        
                                                        
                                                  },
                                            handle: '.drag_me',
                                            
                                    });
                                        
                                 });"""  % dict(X=pos_topics_menu[0], Y=pos_topics_menu[1],
                                                #~ url= URL(r=request, f='ajax_update_user_prefs' ),
                                                url= url_user_prefs,
                                            )
                            )
        )

        
def topics_tree_view_highlight_current():
    """
    highlights current topic in topic tree via javascript
    """
    topic_path_ids = get_topic_path_ids(session.topic_id, include_root=True) if session.topic_id else []
    js =     [
      " $('#topics_tree .topics_path_ids_highlight' ). removeClass('topics_path_ids_highlight');",
      " $('#topics_tree .topics_path_ids_highlight_LAST' ). removeClass('topics_path_ids_highlight_LAST');",
    ]
    if topic_path_ids:
        for tid in topic_path_ids:
            js.append( " $('#topics_tree #node_info_%(topic_id)s .node_name').addClass('topics_path_ids_highlight'); " % dict(topic_id = tid)  )
        js.append( " $('#topics_tree #node_info_%(topic_id)s .node_name').addClass('topics_path_ids_highlight_LAST'); " % dict(topic_id =  topic_path_ids[-1]) )
    return SCRIPT(  '\n'.join(js)   ) 
    #~ return SCRIPT_on_document_ready(  '\n'.join(js)   ) 
    #~ return DIV("HIGHLIGHT", SCRIPT(  "alert('hi');"+'\n'.join(js) + "alert('by');"  ) )
    #~ return DIV("HIGHLIGHT", SCRIPT(  "alert('hi');"   ) )


cache_key_4topics_tree__ = cache_key_4topics_tree()
tt_caching_time = caching_time_get(cache_key_4topics_tree__)
def topics_tree_view():
    #~ return response.render( topics_tree_view_uncached()  )
    try:
        #~ return DIV(XML(topics_tree_view_cached()) , cache_key_4topics_tree__, ':' , tt_caching_time)
        return topics_tree_view_cached()
        #~ return topics_tree_view_noncached()  # NONcached
    except Exception, e:
        response.flash = str(T("Seems to be problems with topics")) +"\n" + str(e) 
        #~ for table in  "Topics Languages".split():

#~ @cache( '--'.join( (request.env.path_info, str(auth.has_permission('update', 'Topics')) ) ) , time_expire=caching_time, cache_model=cache.ram)    
@cache( cache_key_4topics_tree__, time_expire=caching_time_get( cache_key_4topics_tree__ ), cache_model=cache.ram  )   
def topics_tree_view_cached():
    cache_invalidate_mark_done( cache_key_4topics_tree__ )
    #~ cache_track_key(cache_key_4topics_tree__)
    #~ return topics_tree_view_noncached()
    return response.render( topics_tree_view_noncached()  ) # cache the whole rendered html
    #~ return response.render('topics_tree_view.load',  topics_tree_view_noncached()  ) # cache the whole rendered html

def topics_tree_view_rendered():
    return topics_tree_view_noncached()
    #return response.render('topics_tree_view.html',  topics_tree_view_noncached()  ) # cache the whole rendered html
    
def topics_tree_view_noncached():  # can be used to see the noncached situation (when editing topics for example
    data=db(db.Topics.id>0).select() # TODO: !!! maybe needs filtering by current_root_id
    #~ Topics_data = Storage( [(r.id, r)   for r in data] )
    Topics_data = dict( [(r.id, r)   for r in data] )
    Languages = prefetch_db_table_to_dict("Languages")
    Examples_rows = db(db.Examples.id>0).select()
        

    if len(data) == 0:
        return dict(tree={})

    names = extract_field_as_dict(data, 'name')
    names_translated = dict( (topic.id, get_topic_name_translation(topic)) for topic in data )
    
    #~ try:    import rpdb2; rpdb2.start_embedded_debugger('asdf')
    #~ except: pass
        
    tree=topics_tree(data, current_root_id)

    # count filled examples per topic
    example_counts_per_topic = {}
    languages_per_topic ={}
    #~ mylog('Languages CACHED', Languages)
    for row in data:
        #~ nonempty_examples = filter( lambda a: a.code, db(db.Examples.topic_id==row.id ).select() )
        nonempty_examples = filter( lambda a: a.code,
            Examples_rows.find( lambda e: e.topic_id==row.id  )  # filter only needed topic examples
        )
        example_counts_per_topic[row.id] = len( nonempty_examples )
        languages_per_topic[row.id] =  list(set( [
                                                db.Languages[a.lang_id].extension for a in  nonempty_examples
                                                ] ) )
    
    topics_paths = topics_paths_sorted(tree)  # needed for topics_tree_SELECT

    # topic_path_ids are  specific to topic -- so shouldn't be cached
    # topic_path_ids = get_topic_path_ids(session.topic_id, hierarchy_info=tree) if session.topic_id else []
    
    #~ try:
        #~ root_topic = db(db.Topics.name == 'root').select().first()
        #~ if root_topic:
            #~ root_id = root_topic.id
    #~ except:
       #~ bla bla
       
    # moved to view   
    #~ if len(tree.keys()) == 1:  
        #~ root_id = tree.keys()[0]
    #~ else:
        #~ raise Exception("Too many roots in topics_tree_view_uncached()")
        
    #~ return dict(tree=tree, names=names, names_translated=names_translated,
    return  dict(tree=tree, names=names, names_translated=names_translated,
                #~ root_id = root_id,            
                root_id = None,            
                topics_paths=topics_paths,
                example_counts_per_topic=example_counts_per_topic,
                languages_per_topic=languages_per_topic,
                #~ topic_path_ids=topic_path_ids,  # topic_path_ids are  specific to topic -- so shouldn't be cached
                topic_crud = 'edit' if request.env.http_referer and request.env.http_referer.endswith('/edit') else 'view',
                Topics_data=Topics_data,
                )
        
    
#~ def subtopics_ordering_view():
    #~ parent_tid = request.args(0)
    #~ hierarchy_levels = request.args(1) or 1 # for now just 1
    

##################################
#
#   Topics CRUD  adapting
#
####################################
def test():
    return dict( a = DIV(
                        LABEL( INPUT( _type="checkbox", _id="test_trigger", _onclick="alert('kuku'); "), "ku ku" ),
                        #~ SCRIPT(" $('#test_trigger').bind('click', function() {  alert('bu bu');   return true;  });"),
                        DIV( 'click me', _class="test", _onclick= "$('#test_trigger').triggerHandler('click')" 
                        ),
                        SCRIPT_on_document_ready("$('.test').triggerHandler('click');"
                        )
            )
    )

@auth.requires_permission('create', 'Topics')
def subtopic_create():
    try:
        db.Topics.parent_id.default = request.args(0)
        db.Topics.parent_id.writable = False
    except:
        pass
        
    form = crud.create(db.Topics ,
                next=URL(r=request,f='index',args=request.args),
                onaccept= lambda form: cache_invalidate_4topics_tree()  # if we create subtopic, we should invalidate topics_tree...
           )
    #return form
    if request.ajax:
        return form
    return dict(form=form)


@auth.requires_permission('update', 'Topics')
def topic_update():
    try:
        id = int(request.args[0])
        #~ if id==1: # this is deprecated for root
        #~ if db.Topics.parent_id == None:  # this could go
            #~ db.Topics.parent_id.writable = False
            #~ db.Topics.parent_id.represent = lambda id: 'None (because I am Root ;)'
        db.Topics.parent_id.represent = int
    except:
        response.flash = T('Topic not found: given args: %s' % request.args)
        return

    if request.function.startswith('topic_crud'):
        redirect_url=URL(r=request,f='topic_crud',args=[id, 'view'])
    else:
        redirect_url=URL(r=request,f='index',args=request.args)

    db.Topics.changed_fields.update = []    
    form = crud.update( db.Topics, db.Topics[id] , next=redirect_url,
             onaccept=crud.archive, deletable = False
         )
    if form.accepts( request.vars, session ):
        if form.vars.id == form.vars.parent_id:
            #~ form.record.parent_id = None
            db.Topics[id].parent_id = ROOT_FLAG_4PARENT_ID # update 
        cache_invalidate_4topics_tree()
        if 'output' in form.vars.changed_fields:
            for example_in_topic in db(db.Examples.topic_id == id).select(): # 
                if example_in_topic.output_tpl == '' or '@@include topic' in example_in_topic.output_tpl : # if it is empty, topic output is forced, so invalidate in order to see changes
                    cache_invalidate_4example(id=example_in_topic.id, force_get_key=True)  # TODO: still not works...
                    example_in_topic.update_record( output_tpl_valid = False , tpl_invalidator=None , tpl_invalidation_timestamp=request.now ) # batch update would be more efficient for GAE
                    # TODO: maybe propagage invalidation to examples, depending on this example output
        
                    # INVALIDATE   RENDERED
        redirect( redirect_url )
    #return form
    if request.ajax:
        return form
    return dict(form=form)

    
def topic_crud():
    id = request.args(0)
    action = request.args(1) if request.args(1) in 'view edit'.split() else 'view'
    
    if action == 'edit':
        html = topic_update() 
        
    if action == 'view':
        topic = db.Topics[id]
        
        html = DIV (  #crud.read(db.Topics, db.Topics[id]),
                      XML( markdown(topic.explanation)) if topic.explanation else T("doubleclick here to edit topic info...") ,
                     _ondblclick= "web2py_component('%s', '%s')" % ( 
                                    URL( r=request, f=request.function, args=[id, 'edit'] ),
                                    request.function
                                    )   ,
                     )

    return DIV ( html, _class=request.function, _id=request.function ) 
        



    
##################################
#
#   BIG DEAL and Magic -- all examples in topic -- all forms in one page
#
####################################
#~ @cache( cache_key_4topic_examples(), time_expire=caching_time_get(cache_key_4topic_examples() ) , cache_model=cache.ram  )   # be carefull about feedback cached...
# TODO: can't cache unless user prefs for what to show  are updated afterwards via javascript
def topic_examples():
    #if not request.args(0): request.args.append(1) # seems not to work :/
    topic_id = request.args(0) 
    lang_id = 1 # TODO: check why?
    examples=db(db.Examples.topic_id==topic_id).select()
    # make indices
    examples_by_lang = dict( (ex.lang_id, ex) for ex in examples ) # lang_id should be unique
    data__Examples_in_topic = dict( (ex.id, ex) for ex in examples )

    #~ user_prefs_get('shown_example_parts', default_value= set(example_parts2toggle) )


    languages = db(db.Languages.id > 0).select()
    #~ user_prefs_get( 'shown_languages', default_value = set(['Python', 'Java']) )  # if var (shown_languages) not present, init with default_value

    languages_toggling_buttons = []

    languages_init_buttons = []


    ajax_dbg_id = "ajax_dbg" # + lang.name
    crud_action = request.args(1) if request.args(1) in 'view edit'.split()  else 'view'

    form = ''

    example_forms = []
    for lang in languages:
        if lang.id in examples_by_lang:
            # form = example_update_form(examples_by_lang[lang.id])
            example_id = examples_by_lang[lang.id].id
            url_args = [example_id]
            url_function = 'example_update'
        else:  

            url_function = 'example_create'
            url_args = [str(topic_id), str(lang.id)]            

            #insert empty example
            example_id = db.Examples.insert(topic_id=topic_id,
                                            lang_id=lang.id,
                                            author_id=auth.user.id if session.auth else None)
            data__Examples_in_topic[example_id] = db.Examples[example_id] # cache it

        example_LOAD_id = "example_lang_%s_content"%lang.name  # to be able to access 
        #~ if lang.name in user_prefs.shown_languages: could be checked for optimization -- now is checked at example_forms.append
        if crud_action == 'edit':
            #~ form = LOAD(request.controller, 'example_update', args=[example_id], ajax=True, target=example_LOAD_id) #, ajax_trap=True)
            example_LOAD_URL = URL(r=request, f='example_update', args=[example_id]) #, ajax_trap=True)
            form = LOAD(url=example_LOAD_URL, ajax=True, target=example_LOAD_id) #, ajax_trap=True)
        elif crud_action == 'view':
            example_LOAD_URL = URL(r=request, f='example_view_4url.load', args=[example_id])
            form = LOAD(url=example_LOAD_URL,
                        target = example_LOAD_id,
                        ajax= True # when caching this helps have individual id=path_info for each example
                        #~ ajax = not (lang.name in user_prefs.shown_languages)  # load shown languages directly
                   )    

        if not lang.name in user_prefs.shown_languages:
            form = LAZY_LOAD_TARGET(example_LOAD_id, example_LOAD_URL)

        example_forms.append( DIV(
                                  DIV(lang.name, _class="lang_name"),
                                  form,
                                  _id="example_lang_%s"%lang.name , 
                                  _style="display:%s"% ('block' if lang.name in user_prefs.shown_languages else 'none'),
                                  _class="example"
                                  )
                            )

        languages_init_buttons.append(
            (            
                TOGGLE_anywhere_by_user_pref(
                        #~ selector=example_LOAD_id,
                        selector="#", selector_txt_4escaping="example_lang_%s"%lang.name ,
                        var='lang_%s'%lang.id, msg=T("Toggle %s" % lang.name), init_val="true" if lang.name in user_prefs.shown_languages else 'false'
                ) ,
                LAZY_LOAD_triggering_inject(trigger_id='toggle_lang_%s'%lang.id, target_id=example_LOAD_id, )
            )
        )
        
        languages_toggling_buttons.append( 
            LABEL(
                INPUT(_type='checkbox',_id='toggle_lang_'+lang.name,
                      _onclick= ("""lang_name='%s';
                                    target_id='example_lang_'+lang_name;                                    
                                    target_id_escaped=$.escape(target_id);
                                    
                                    $('#'+target_id_escaped).toggle();  // better show/hide by attr('checked')=='checked' or 'value'==on ??

                                    target_content_id='example_lang_'+lang_name+'_content';                                    
                                    target_content_id_escaped=$.escape(target_content_id);
                                    
                                    $('#'+target_content_id_escaped).
                                        each( function(index) {
                                            if ($(this).text()=='') { // if nothing loaded still
                                                 //web2py_component(action,  target);
                                                 //alert($(this).data('action'));
                                                 web2py_component( $(this).data('action') ,  $(this).attr('id') ); // load it 
                                            }
                                        } );                             
                                  """ %lang.name) +
                                
                                ("url='%s/'+this.checked; ajax(url, [], '%s');" % (URL(r=request, f='ajax_update_shown_languages', args =[lang.id]), ajax_dbg_id) ) ,

                      value = 'on' if lang.name in user_prefs.shown_languages else '',
                     ),
               lang.name  + ('* (example available)' if example_has_info(example_id, data__Examples = data__Examples_in_topic) else '') # asterix means available example
               ,
               #SPAN(_id=ajax_dbg_id) #"ajax_dbg_active_langs_"+lang.name)
            )
        )

    # Example Parts Toggling
    example_parts_toggling_buttons = []
    for part_name in example_parts2toggle:
        example_parts_toggling_buttons.append(
            LABEL(
                INPUT(_type='checkbox', _class="toggle_example_part", _id='toggle_example_part_'+part_name,
                      # workaround for language toggling to update stuff ##TODO: test on other browsers -- after language toggling fields might lose synch
                      _onmouseover= ("""var class_name_selector = '.'+'%s';
                                        if (this.checked) $(class_name_selector).show() ;
                                        else $(class_name_selector).hide() ;
                                        //alert('mouseover: '+this.id);
                                    """ %(part_name )) ,
                      _onclick= (" $('.%s').toggle();" % (part_name )) +
                                
                                ("url='%s/'+this.checked; ajax(url, [], '%s');" % \
                                    (URL(r=request, f='ajax_update_shown_example_parts', args =[part_name]), ajax_dbg_id)
                                ),
                                value = 'on' if part_name in user_prefs.shown_example_parts else '',
                     ),
                SCRIPT( "$('#%s').data('targets_selector', '.%s')" %
                                                ('toggle_example_part_'+part_name, part_name )
                    ), 
               example_parts_human[part_name]
            )
        )
        
            
    ## FIELDS toggling    
    main_fields_tr_ids = ["Examples_%s__row" % a for a in db.ExamplesKernel.fields]
    tr_ids_str4search = '/%s/' % '/'.join(main_fields_tr_ids)
    
    nonmainfields_toggling = INPUT(_type='button',_value='toggle non-main fields', 
    _onclick="""            
            $('form  tr').each(function(){ 
                var this_id = $(this).attr('id');
                var names2exclude = '%s';
                if ( (this_id.search('Examples_') == 0) ){ 
                        if (     (names2exclude.search('/'+this_id+'/') < 0) )
                        {   $(this).toggle(); //alert(this_id);
                        }
                }
            });  
            """ % tr_ids_str4search 
                )

    kwargs4edit_mode = dict(a2_nonmain_fields_toggling = nonmainfields_toggling) if crud_action == 'edit' else {}

    session.topic_id = topic_id

    response.user_prefs_menu.append(
                           ( T("What to show?"),
                             [
                               ( T("Languages"),  [ (  DIV(  map(DIV, languages_toggling_buttons), _class="content-in-sf"),  ) ] ),
   
                               ( T("Example Parts"),  [(  DIV( map(DIV, example_parts_toggling_buttons ), _class="content-in-sf"),  )] ),
                               
                                ( TOGGLE_anywhere_by_user_pref('.feedback', 'display_feedback_all', msg=T("All Feedback") )  ) ,
                                ( TOGGLE_anywhere_by_user_pref('junk_caching', 'use_caching', msg=T("Use Caching") )  ) ,
                                #~ ( user_pref_factory.checkbox (  msg=T("Show/hide All Feedback") , 'display_feedback',) )  ,
                             ]
                           )                              
     )                           
                        

    return_dict = dict(
                 #~ a00_Toggling_edit_or_view = toggle_crud_action if auth.has_permission('update', 'Topics') else '',

                 #~ a1_languages_init_buttons =  languages_init_buttons ,
                 
                 a01_topic_path = H2(  
                                               [  SPAN( topic_path_sep,
                                                    A( get_topic_name_translation(db.Topics[tid]) , _href=URL(r=request, f='topic_examples', args=[tid, crud_action] ) ) ) \
                                                    for tid in get_topic_path_ids(topic_id)
                                               ]
                                              )    ,
                                            
                                            

                 #~ b0_topics_tree  =  topics_tree_dragabble_togglegable() ,
                 # workaround to get topics_tree at the top of page, but load it only after the other stuff ;)
                 a02_topics_tree  = (
                                        DIV( _id='topics_tree_draggable_togglable_container'),
                                        LAZY_LOAD_directly_to_existing_target('topics_tree_draggable_togglable_container', URL(r=request, c=request.controller, f='topics_tree_dragabble_togglable.load' ) )
                                    ),
                        

                 a1_Topic_info = DIV (
                                        #~ LOAD( c=request.controller, f='topic_crud', args=request.args, extension='load', ajax=True ),
                                        LOAD( c=request.controller, f='topic_crud', args=request.args, extension='load', ajax=False ),
                                        BR(),
                                        TOGGLE(
                                            (
                                                plugin_rating(tablename='Topics', record_id=topic_id),
                                                #~ plugin_wiki.widget('comments',table='Topics', record_id=topic_id),
                                                plugin_comments(tablename='Topics', record_id=topic_id),
                                            ),
                                            msg=T('Feedback on topic'),
                                            stuff_class='feedback',
                                            ),
                                         BR(),
                                            
                                    ),     


                 a31_Examples =TABLE(TR( *example_forms ), _id="examples_for_topic"), 

                 a40_Instructions = T("Doubleclick on example parts to edit them"),
                 

                 a9_dbg_ajax = SPAN(_id=ajax_dbg_id, _name=ajax_dbg_id ),
                 **kwargs4edit_mode
                 
            )
            #**add_example(topic_id) ) # , **add_example(topic_id)

    return response.render(return_dict) 
    
def quote_for_js_code_formatting(dictionary, quote="'"):
    for key, val in dictionary.items():
        #~ try:
            #~ val = int(val)
            #~ continue    # if value is quoted integer , skip it 
        #~ except: pass

        if isinstance( val, str ):
            if not val in 'true false null'.split(): # except logical values
                dictionary[key] = quote + val + quote
    return dictinary # actually return is not needed, as dict reference doesn't change

def jscode_user_prefs_get(var, default_value=None):
    value = user_prefs_get(var, default_value)
    return """
    user_prefs['%(var)s'] = %(value)s;
    """    % quote_for_js_code_formatting(dict(var=var, value=value)) \
        .update( dict(url_user_prefs_get=URL(r=request, f='ajax_user_prefs_get') ) )
        
#~ def user_prefs_js_get_code(var, default_value=None):
    #~ value = user_prefs_get(var, default_value)
#~ 
    #~ return """
    #~ // if (user_prefs == undefined)
    #~ // var user_prefs = {};
    #~ /*
    #~ function user_prefs_set(var_name, value){
        #~ user_prefs[var_name] = value;
    #~ }
    #~ function user_prefs_get(var_name){
        #~ if (user_prefs[var_name] == undefined ){
            #~ // try to get it from server (session or db)
            #~ value = ajax_user_prefs_get('%(url_user_prefs_get)s/'+ var_name )
            #~ user_prefs[var_name] = value;
            #~ return value;
        #~ }
        #~ else return user_prefs[var_name];
    #~ }
    #~ */
    #~ // user_prefs_set('%(var)s', %(value)s);
    #~ 
    #~ user_prefs['%(var)s'] = %(value)s;
    #~ """    % quote_for_js_code_formatting(dict(var=var, value=value)) \
        #~ .update( dict(url_user_prefs_get=URL(r=request, f='ajax_user_prefs_get') ) )
    #~ 
    #~ return """
    #~ var %(var)s = %(value)s;
    #~ """    % locals()

def ajax_update_user_prefs():
    var = request.args(0)
    value = request.args[1:] or ''
    if len(value) == 1:
        value = value[0]
    if var:
        user_prefs_set(var, value)
        return "session.user_prefs['%s'] = '%s'" % (var, value)

#~ def ajax_toggle_session_vars():
    #~ """
    #~ stores variable values as True/False
    #~ and toggles them on request
    #~ """
    #~ var = request.args(0)
    #~ first_time_expected_value = request.args(1) or False
    #~ if var:
        #~ new_value = not bool(session.get(var, first_time_expected_value))
        #~ session[var] = new_value
    
def ajax_update_shown_example_parts():
    part_name = request.args(0)
    is_shown = request.args(1) or ''

    if is_shown=='true': # 'true' is got form javascript this.checked property
        user_prefs.shown_example_parts.add(part_name)
    else:
        if part_name in user_prefs.shown_example_parts:
            user_prefs.shown_example_parts.remove(part_name)
    user_prefs_store('shown_example_parts')

    
def ajax_update_shown_languages():    
    #~ from urllib import unquote
    lang_id = request.args(0)
    #~ lang_name = unquote(request.args(0)) # passing lang_name by URL is problem with "C++"
    lang_name = db.Languages[lang_id].name
    is_shown = request.args(1)
    
    if is_shown=='true': # 'true' is got form javascript this.checked property
        user_prefs.shown_languages.add(lang_name)
    else:
        try:
            user_prefs.shown_languages.remove(lang_name)
        except:
            pass
    user_prefs_store('shown_languages')


@auth.requires_permission('update', 'Topics')
def ajax_subtopics_order_update():
    subtopics_ids = request.vars['subtopic[]'];
    if subtopics_ids:
        for nr, id in enumerate( subtopics_ids ):
            db.Topics[id].update_record(order_in_topic = nr)
        cache_invalidate_4topics_tree()
    return str(subtopics_ids) + ' --> cache_invalidate_4topics_tree()'
    

def get_parent_topic_example_4same_language(id):
    # find the parent of current topic
    parent_topic_id = db.Topics [db.Examples[id].topic_id]  .parent_id
    # get example from the parent with same language (! and someday later for the same author)
    result = db( (db.Examples.topic_id == db.Topics[parent_topic_id].id ) &
        (db.Examples.lang_id == db.Examples[id].lang_id ) 
        #& (db.Examples.author == db.Examples[id].author)
      ).select()
    if result:
        return result[0]
    else:
        return None



def check_unique_topic_lang_auth(topic_id, lang_id, author_id=None):
    #if author_id: ....
    #db(db.Examples.topic_id == topic_id  &  db.Examples.lang_id == lang_id  &  db.Examples.author_id == author_id).select()
    rows = db(db.Examples.topic_id == topic_id  &  db.Examples.lang_id == lang_id ).select()
    if len(rows) > 1:
        return (T('Not unique language for this topic/example'), len(rows) ) 


##################################
#
#   Examples CRUD adapting
#
####################################
def parse_code_templates(example, field,
                        include_levels=-1,
                        #~ first_call=True, # a workaround for invalidating the current template
                        update_fields=True,
                        
                        parent_depends_on_ids_list=None,

                        regenerate=True, goon_regenerating_only_older_than=None,

                        inclusions_dbg_info=False,
                        inclusions_dbg_levels_to_wrap=0,
                        #~ callback4each_plain_line=lambda x: x   # used for code_highlighting when inclusions_dbg_info = True
                        callback4each_plain_line=None   # used for code_highlighting when inclusions_dbg_info = True
                        ):
    """
    recursively parses pre/postcode_templates to make up the full'y functioning program for code
    should first look for generated field, and if it is not valid (empty/too old or so) parse field_tpl
    output_tpl could also include output defined in topic.output
    if force_regenerate is True -- we should always take precode_tpl or postcode_tpl instead of precode/postcode (maybe also go till root?)
    one more way is to parse only the older_than some timestamp examples

    include_levels -- how many levels to recurse, -1 (or any negative) means recruse ALL till root
    update_fields - fielsd will be updated with generated values (if new value is generated). In most cases it is sound to leave it True.
    regenerate - values for all levels will be generated (not taken the ones that are present)
    goon_regenerating_only_older_than - should regenerate only values older than sth... But this is tricky (and not implemented yet) -- as if we have older in second recursion level, but in first everything is ok, we don't see this too old stuff (if we use simple recursion)
    empty values are always being regenerated and updated

    inclusions_dbg_info -- helpfull when editing tpl, should show where and what is inserted, forses "regenerate"
    """    
    # if field.endswith('_tpl')
      # we can include any of parts: pre_code/code/post_code or output
      # usage:
      # @@include example_topic_name
      # @@include example_topic_name[.pre_code|code|post_code]
      # TODO TOTHINK: best is to include only same field/code_part, because invalidation is error-prone otherwise...
      # by default it should include the same part as current 
      # by default it would include parent topic example
      # @@include 
      # @@include .pre_code
      # or the same:
      # @@include __parent__
      # @@include __parent__.pre_code
      #
      # there might be confusions when using personalized examples (if their names differ from public)
      # so maybe
      # @@include example_topic_name#id
      # or
      # @@include example_topic_name*author
      #
      # output_tpl could also use
      # @@include topic.output
      #
      # template actions need commenting/explanations: comment_mark4tpl_actions='///' # tripple slash


    def is_tpl_valid():
        return example[ field+"_tpl_valid" ]  # field is taken from parse_code_templates(example, field, ...

    def set_tpl_valid(value):
        exapmle.update_record(  ** { field+"_tpl_valid" : value } )
        
    I_depend_on_ids_list = [] # to handle multiple inheritance ok
    
    if parent_depends_on_ids_list==None:
        parent_depends_on_ids_list = []  # should be updated before return !!!

    parent_depends_on_ids_list.append(example.id)   # simple truth :) # commenting out, because when exporting/importing to GAE self references get lost http://groups.google.com/group/web2py/browse_thread/thread/0a61b01c9a4c7b02#
    
    if example[field+'_tpl_depends_on']==None: # for created before introducing tpl_depends_on 
        example.update_record( **{ field+'_tpl_depends_on' : []  })

    
    if inclusions_dbg_info:
        regenerate = True
        update_fields = False # ??
        
    # if we have plain field  (not template) # TODO: rethink (as plain fields are intensively used for dbg_info
    if not field.endswith('_tpl'):
        # and no special regenerations are required
        if not (regenerate or goon_regenerating_only_older_than ): #  \
           #~ or example[field+'_tpl'].strip()=='' and  example[field].strip()=='' : # TODO: WAS SOME BUG
            # we just return it
            parent_depends_on_ids_list.append( example.id )
            parent_depends_on_ids_list.extend( example[field+'_tpl_depends_on'] )
            # tpl_valid -- not sure here
            return example[field] if not callback4each_plain_line else XML( callback4each_plain_line(example[field]) )

    # otherwise let's prepair and go parsing
    # in case we got field name wit _tpl, adapt it a bit
    if field.endswith('_tpl'):
        field = field[:-len('_tpl')]
        
    field_tpl = field+'_tpl'

    #~ if db.Topics[example.topic_id] == None:
        #~ mylog( "ERROR parsing templates. db.Topics[example.topic_id] == None.\n probably ID incorrect", dict(example_id=example.id) )

    _stop_ = False    
    # recursion stop
    if is_tpl_valid() and not inclusions_dbg_info: # by current field 
        regenerate = False  ## hope won't cause problems
        parent_depends_on_ids_list.append( example.id )
        parent_depends_on_ids_list.extend( example[field+'_tpl_depends_on'] )
        _stop_ = True
        
    #~ import rpdb2; rpdb2.start_embedded_debugger('asdf')

    #~ if example.topic_id == example.topic_id.parent_id:  # if root -- 
    if is_topic_root( example.topic_id ) :  # if root -- 
        if field=='output':  #output could include topic info...
            pass      # for the first time
        else:
            example.update_record( **{field : example[field_tpl]}  ) # just update -- should be no includes
            cache_invalidate_4example(example.id)
            
            _stop_ = True

    try:
        if parent_depends_on_ids_list[-1] == parent_depends_on_ids_list[-2]:  # we are in infinite recursion --
                                                                # maybe except case when fields are different
                                                                # root output could use this for @@include topic.output 
            _stop_ = True
    except:
        pass
        
    # old recursion stop : probably not very important now
    if include_levels == 0  \
    or is_topic_root(db.Topics[example.topic_id]):        
    #~ or example.topic_id == example.topic_id.parent_id:        
    #~ or example.topic_id == root_id \
    #~ or db.Topics[example.topic_id].name.lower().strip()== 'root' \
        # tpl_valid -- not sure here
        #~ return example[field_tpl] if not callback4each_plain_line else XML( callback4each_plain_line(example[field_tpl]) )
        #~ return example[field]  if not callback4each_plain_line else XML( callback4each_plain_line(example[field]) )
        _stop_ = True

    if _stop_:
        return example[field] if not callback4each_plain_line else XML( callback4each_plain_line(example[field]) )

    #~ print "=== parse_code_templates(example, field, include_levels, ...)", [example.id, example[field], db.Languages[example.lang_id].name ], field, include_levels
    kwargs2pass = dict( update_fields=update_fields,
                        #~ depends_on_ids_list=depends_on_ids_list,  # very important :)
                        parent_depends_on_ids_list=I_depend_on_ids_list,  # very important :)
                                                                  # but might be not valid for branched inclusion -- multiple inheritance
                        regenerate=regenerate,
                        goon_regenerating_only_older_than=goon_regenerating_only_older_than,
                        inclusions_dbg_info=inclusions_dbg_info,
                        inclusions_dbg_levels_to_wrap=inclusions_dbg_levels_to_wrap-1,
                        callback4each_plain_line=callback4each_plain_line
                      )



    #~ print example[field_tpl]
    
    #~ if not field in example:
        #~ response.flash = T("Error: field %s not available in example %s") %(field, example)
        #~ return None
    mylog("START PARSE CODE",  field, example.topic_id.name, example.lang_id.name ) #, example.as_dict )

    source_text = example[field_tpl] or ''
    result_lines = []
    

    #~ def not_all_templates_applied(lines):
        #~ for line in lines: if line.startswith('@@') return True

    #~ while level < include_levels_max \
      #~ and not_all_templates_applied(result_lines):
    #~ example[field_tpl] = example[field_tpl] or ''
    try:
        lines = source_text.split('\n')      # we should parse only *_tpl fields!!
    except Exception, e:
        mylog ( "Error with source_text when applying templates, example_id, field:", example.id, field_tpl, msg=str(e) ) 
        return

    #~ print "source_text", source_text
    #~ print "lines", lines
    comment_mark4tpl_actions = '///'
    for line in lines:
        
        if line.startswith('@@'):
            line = line.split(comment_mark4tpl_actions)[0]  # trim tpl comments 
            #~ mylog( line )
            command, arguments = None, ''
            command_parts = line[2:].split()  # TODO: what if there are more whitespace after @@include
            if len(command_parts) > 0:
                command = command_parts[0]
            else:
                continue

            # arguments stuff
            if len(command_parts) > 1:  arguments = command_parts[1]
            topic_name, example_id, author = None, None, None
            example_part = field
            if session.auth:
                author = auth.user.email  # should make it unique, or use email / lastname / nickname ...
                
            # lets parse    
            if '#' in arguments: arguments, example_id = arguments.split('#')
            if '*' in arguments: arguments, author = arguments.split('*')
            if '.' in arguments: arguments, example_part = arguments.split('.')
            topic_name = arguments

            ### BY Example ID
            if example_id:
                mylog( "by Example ID",  line )
                #~ result_lines.append( db.Examples[example_id][example_part])
                result_line = parse_code_templates(
                                            db.Examples[example_id], example_part,
                                            include_levels=include_levels-1,
                                            **kwargs2pass
                              )
                parent_depends_on_ids_list.extend( I_depend_on_ids_list ) # update with new info from this inclusion/inheritance branch


                result_lines.append( result_line )
                                    
                continue

            ### special case for 'output': it can include output defined in topic (only his direct topic -- that shoud be enough)
            if field=='output' and example_part=='output' and topic_name == 'topic':
                result_lines.append( db.Topics[example.topic_id].output )
                continue

                
            ### BY Topic and author

            try:
                author_id = db(db.auth_user.email == author.strip()).select().first().id
            except Exception, e:
                mylog(  "Authorship can't be determined by: ", author,  msg=str(e) )
                author_id = None

            #~ if not author_id:
                #~ author_id = db(db.auth_user.email == user.configs.preferred_author).select().first().id

            
            if topic_name.strip() in (None, '', '__parent__'):
                topic_id = db.Topics[example.topic_id].parent_id
            else:
                topic_id = db(db.Topics.name == topic_name ).select().first().id
            if not topic_id:
                mylog(  "ERROR, example not found by line:", line )

                
            tpl_examples = db( (db.Examples.topic_id==int(topic_id)) & (db.Examples.lang_id == example.lang_id) ).select()
            if len(tpl_examples) > 1 and author_id:
                tpl_examples_by_author = db(
                                 (db.Examples.topic_id==topic_id)  & (db.Examples.author_id == author_id) & (db.Examples.lang_id == example.lang_id)
                                ).select()
                if len(tpl_examples_by_author):
                    tpl_examples = tpl_examples_by_author
                    
            if len(tpl_examples) == 0:
                mylog(  "ERROR, example not found by line, example_id, topic_id, author_id", line, example.id, topic_id, author_id )
                continue

            #~ print 'DBG',  example.id, field, line, '-->', example_part, topic_id, db.Topics[topic_id].name, author_id
            #~ for row in tpl_examples:
                #~ print row.id, example_part, row[example_part], \
                      #~ db.Topics[row.topic_id].name if db.Topics[row.topic_id] else  row.topic_id  , \
                      #~ db.Languages[row.lang_id].name if db.Languages[row.lang_id] else row.lang_id

            chosen_tpl_example = tpl_examples.first()
            #~ result_lines.append( chosen_tpl_example[example_part])
            result_line = parse_code_templates(
                                            chosen_tpl_example, example_part,
                                            include_levels=include_levels-1,
                                            **kwargs2pass
                                            )
            parent_depends_on_ids_list.extend( I_depend_on_ids_list ) # update with new info from this inclusion/inheritance branch

            if inclusions_dbg_info:  # TODO: make DRY - now stuff repeats in other place
                dbg_line_id = "%s_%s_%s" % ( db.Topics[example.topic_id].name,
                                                     field,
                                                     example.id )
                                                     
                result_line_id = 'result_line_%s' % (dbg_line_id )
                result_line = DIV(
                                DIV( line+' (%s)' % (dbg_line_id), _onclick="$('#%s').toggle(); " % (result_line_id ) , _class='', _style='font-size:0.8em; cursor:pointer'), # TODO: unhardcode styles
                                DIV( XML(result_line), _id=result_line_id,  _style="margin:4px; border: 1px solid silver; " + \
                                                                       ( "display:none;" if inclusions_dbg_levels_to_wrap == 0 else '' )     ),
                                )
                #~ result_line = result_line.xml() 
                                   

            result_lines.append( result_line )
                                
                

        else:
            if callback4each_plain_line:
                line = XML( callback4each_plain_line(line) )
            #~ line = callback4each_plain_line(line)
            result_lines.append(line)

    if inclusions_dbg_info:
        #~ print result_lines
        #~ return '\n'.join(map(str,result_lines))  # prevent problems with join span...
        
        #~ return map(XML, result_lines)  # prevent problems with join span...
        #~ result_lines = map( XML, result_lines )

       #~ result_lines = map( lambda x: x.xml() or '', result_lines )
       return  DIV(result_lines).xml()
       #~ result = u'\n<br/>'.join( result_lines)
        #~ return XML( result )
         
        
    #~ print 'result_lines', result_lines
    #~ result = '\n'.join( filter(lambda x: x!=None, result_lines)   )
    result_lines = map( lambda x: x or '', result_lines )
    result = '\n'.join( result_lines)  

    
    if update_fields:
        #~ target_field = field[:-len('_tpl')] if field.endswith('_tpl') else field  # we shoould leave *_tplintact
        if field in 'pre_code post_code output'.split():
            example.update_record( **{field: result,
                                    field+'_timestamp' : request.now,
                                    #~ 'tpl_depends_on': list(set(depends_on_ids_list)),  # for dependancies and
                                    field+'_tpl_depends_on': list(set(I_depend_on_ids_list)),  # for dependancies and
                                    field+'_tpl_valid': True                       # need to regenerate tracking !!!
                                      } )
            cache_invalidate_4example(example.id)
                                      
        else:
            mylog(  "WARNING, parse_code_templates->update_fields,  we should only update precode or postcode, but not", field, )
            
    return result

def get_archive_items_4url():
    defaults = (None, None, 1, 'Examples')
    args = [defaults[i] if a=='_' else a for (i, a) in enumerate(request.args) ]
    #~ return str(args)
    return get_archive_items(*args)

    
def get_archive_items(current_record=None, author_id=None, limit_the_last_ones=1, table='Examples'):
    #~ if not current_record and not author_id:
        #~ return []
    archive = "%s_archive" % table
    q1 = db[archive].current_record==current_record 
    q2 = db[archive].author_id==author_id
    q = db[archive].id > 0  # or True?
    # now join these queries with AND -- omit the ones, that don't have what to say
    if current_record: q = q & q1
    if author_id: q = q & q2
    
    #~ queries =[]
    #~ for field in 'current_record author_id'.split():
        #~ if locals()[field]:
            #~ queries.append(db[archive][field]==locals()[field]
    try: limit_the_last_ones = int(limit_the_last_ones)
    except: pass
    try:
        rows = db(q).select(orderby=~db[archive].id, limitby=(0, limit_the_last_ones) )
    except Exception, e:
        return dict(msg="ERROR: get_archive_items: " + str(e))
    return rows

cache_key_4example__ =cache_key_4example()
#~ response.flash += '\n' + cache_key_4example__
@cache(   cache_key_4example__,   time_expire=caching_time_get(cache_key_4example__ ) , cache_model=cache.ram      )    
def example_view_4url():
    cache_invalidate_mark_done(cache_key_4example__)
    #~ cache_track_key(cache_key_4example__)   # is inside cache_invalidate_mark_done
    id = int(request.args(0))
    #~ return example_view(id) 
    #~ return response.render(  example_view(id) )
    
    return response.render( '{{=view}}', dict(view = example_view(id) ) )  # should render what to show options to be set in js if html is cached

    # d = dict(time=t, link=A('click to reload', _href=request.url))
    # return response.render(d)
    
def example_view(example):
    mylog()  #for parse_code_templates output debugging
    if isinstance(example, int):
        example = db(db.Examples.id == example).select().first()
    ## could also insert comments:   (comment(example.lang_id, T('Pre Code'))  , ... T('Main Example Code'), ... T('ending'), 
    try: author_sign = db.auth_user[example.author_id].email
    except: author_sign = None
    example_view_id = "example_view_%s" % example.id
    content = DIV(
                  DIV(   example_part_view(example.id, 'explanation'),
                         example_part_view(example.id, 'full_code'),
                         HR(),
                         example_part_view(example.id, 'output'),

                        _class = "main_info", _id="maint_info_%s" % example.id
                  ),

                  UL( 
                      ', '.join( (request.env.path_info, str(author_sign), str(example.timestamp) ) ), 
                        #~ 'Code generation info:', example.lang_id.name,

                       'Last change: %s, %s' % (example.comment, example.changed_fields),
                        # weird way to add LAZY loadable togglable div 
                        #~ LAZY_LOAD_and_TOGGLE(
                        TOGGLE(
                                msg= XML(B(T("Changelog ")) + T("(except last one)")+ " ..."), _id='toggler_changelog_example_%s'%example.id,
                                stuff='', stuff_id='changelog_example_%s' % example.id, stuff_class="changelog",
                                bind_user_prefs=False,
                                _onclick = """
                                if ($('#%(target_id)s').text()=='') {
                                             web2py_component(  '%(url)s' ,  '%(target_id)s' );
                                }
                                """ % dict(target_id='changelog_example_%s'%example.id, url=URL(r=request, f='example_changelog', args=[example.id]) ),
                               ),
                        DIV( 'Invalidation: %s,  %s' % ( example.tpl_invalidator, example.tpl_invalidation_timestamp ),
                             UL([
                                "%s: tpl_valid=%s, tpl_depends_on=%s" % (field, example[field+'_tpl_valid'], example[field+'_tpl_depends_on']) for field in 'pre_code post_code output'.split()
                              ]) ,
                            ),
                    _class = "meta_info",
                    _style="display:%s" %  ('block' if "meta_info" in user_prefs.shown_example_parts else 'none'),                     
                ),
        )                  
                  

    if not example.language_can_do_this:
        #~ content = DIV( T("This language doesn't support this feature"),  DIV( content, _class="language_cannot_do_this" ) )
        #~ content =  T("This language doesn't support this feature")
        content = example_part_view(example.id, 'language_can_do_this')

    return DIV(
                    content,
                
                    # Feedback                    
                    TOGGLE( 
                        DIV(
                        (
                            #~ plugin_rating(tablename="Examples",record_id=example.id),
                            # WE CAN'T HAVE MORE THAN ONE RATING PER PAGE :/
                            plugin_rating(tablename='Examples', record_id=example.id),

                            #~ plugin_wiki.widget('youtube',code='l7AWnfFRc7g'),
                            #~ comments(table='Examples', record_id=example.id),
                            # PROBLEM: starts saving only after double submit (per page load)
                            # PROBLEM: doesn't load, if a user is not logged in
                            #~ plugin_wiki.widget('comments',table='Examples', record_id=example.id),

                            # plugin_comments seems to work ok
                            plugin_comments(tablename='Examples', record_id=example.id),

                         ),
                                                 

                        ),
                        #~ msg = str(T("Feedback on %s example")) % db.Languages[example.lang_id].name,
                        msg = T("Feedback"),
                        stuff_id = "feedback_example_lang_%s" % db.Languages[example.lang_id].id,
                        stuff_class = "feedback",
                        #~ default_display='block' if user_prefs_get('display_feedback_all')=='true' else 'none',                        
                        default_display='block' if auth.user_id or user_prefs_get('display_feedback_all')=='true' else 'none',

                        _style = "display: %s" % ('block' if user_prefs_get('display_feedback_all')=='true' else 'none'),
                        _class = "toggling_container feedback"
                    ),

                # call update shown parts    
                #~ SCRIPT ( "$('.toggle_example_part').trigger('mouseover');  " ),
                SCRIPT ( "set_visibility_by_checkbox_with_targets_in_data('.toggle_example_part'); " ),

                _id = example_view_id,
           )


def analyse_args_about_fields():
    id = request.args(0)
    example=db.Examples[id]
    fields = request.args[1:] #usually it will be just one field -- but for more general approach imagine its list
    main_field = fields[0]
    if main_field == 'full_code':  fields = [ 'pre_code', 'code', 'post_code', ]
    #~ if main_field == 'full_code':  fields = ['pre_code_tpl', 'pre_code', 'code', 'post_code_tpl', 'post_code', ]
    #~ if main_field in ['pre_code', 'post_code']:  fields = ['pre_code_tpl', 'pre_code',  'post_code_tpl', 'post_code', ]
    #~ if main_field == 'output':  fields = [ 'output_tpl', 'output', ] # TODO: _tpl fields could be togglible
    return id, main_field, fields


def TOGGLE_anywhere_by_user_pref(selector, var, selector_txt_4escaping='', msg=T("Toggle"), init_val="false"):
    """
    show + update user_prefs  true/false
    and toggle something by it
    """
    #~ show_or_hide = {'true':'show', 'false':'hide'}
    init_val = user_prefs_get(var, init_val)  # get value from session or use given
    #~ url = URL(r=request, f='ajax_toggle_session_vars', args=[var])
    #~ url = URL(r=request, f='ajax_update_user_prefs' ) #, args=[var])
    url = url_user_prefs
    return (
                    
                    DIV(
                        LABEL(
                            INPUT(_type='checkbox',_id='toggle_'+var,
                                value = 'on' if init_val=='true' else '',
                                #~ _onclick="$('%s').%s();" % ( selector, show_or_hide, )
                                _onclick="""
                                %(var)s = ! %(var)s;
                                set_state_%(var)s();
                                    
                                var url='%(url)s/%(var)s/'+%(var)s;  //one var is for name and one is for value from js namespace ;)
                                ajax(url, [], 'ajax_dbg');
                                
                                """ %  locals() ,
                            ),
                            msg,
 
                        _class="toggling",
                        )                        
                    ),    
                    SCRIPT("""
                        var %(var)s = %(init_val)s;
                        
                        function set_state_%(var)s(){
                            if ( %(var)s ){
                                $('%(selector)s'+$.escape('%(selector_txt_4escaping)s') ).show();
                                $('toggle_%(var)s').attr('checked', 'checked');
                                
                            }
                            else {
                                $('%(selector)s'+$.escape('%(selector_txt_4escaping)s')).hide();
                                $('toggle_%(var)s').attr('checked', 'none');
                            }
                        };
                        
                        //~ set_state_%(var)s();  // document on load
                        $(document).ready(function() {
                            set_state_%(var)s();  
                        });
                        """ % locals()
                    ),

            )
    
def TOGGLE(stuff, msg=None, stuff_id=None, stuff_class="stuff",
           levels=1, default_display='none', bind_user_prefs=True, **kwargs):
               
    if levels <= 0: # no need to apply toggleing 
        return stuff        
            
    if isinstance(stuff, dict): #if we get  dict, loop through it
        result = []
        for key, value in stuff.items:
            result.append( TOGGLE( value, msg=key, stuff_class=stuff_class, default_display='block', levels=levels-1) )
        return result
        
    else:
        if not stuff_id:
            if not msg:
                msg = "Toggle stuff " + str(request.now)
            stuff_id = escape4dom(str(msg))
            
        
        extra_onclick = kwargs.pop('_onclick') if '_onclick' in kwargs else '' # would also TODO: _class _title, _style
        _onclick= ( "$('#%s').toggle();" % stuff_id )
        if bind_user_prefs:
            _onclick += \
            ("el=document.getElementById('%s');  url='%s/display_%s/'+el.style.display; ajax(url, [], 'ajax_dbg'); " % \
              #~ (stuff_id, URL(r=request, f='ajax_update_user_prefs'), stuff_id)
              (stuff_id, url_user_prefs, stuff_id)
            )
        _onclick += extra_onclick
        
        return DIV(
            DIV( msg,
                # could also save toggling state in session 
                _class = "toggling" + " " +stuff_class,                
                _title = T("Click to show/hide"),
                _onclick = _onclick,            
            ),
            DIV( stuff,  _id=stuff_id , _class=stuff_class,
                 _style="display: %s" % ( user_prefs.get("display_%s" % stuff_id, default_display) if bind_user_prefs else default_display) # hidden by default
            ),

            **kwargs

            )

def DIVify_2edit_dblcicked(example, field, content):
    div_id = _id="%s_%s" % (field,  example.id)
    return DIV(
                DIV(  # we wrap one more div -- for outer div to leave undblclickable
                    content,
                     #~ _ondblclick= makejs_ajax_edit_text_field(example, field),
                     _ondblclick= "web2py_component('%s', '%s')" % ( 
                                    URL(r=request, f="example_part_edit", args=[example.id, field] ),
                                    div_id
                                    )   ,
                 ),
                _id=div_id  , _class=field,
                _style="display:%s" %  ('block' if field in user_prefs.shown_example_parts else 'none'), 
        )     

        
def makejs_ajax_edit_text_field(example, field, target_id=None):
    """
    TODO: deprecate in favour of web2py_component
    """
    target_id = target_id or field+'_%s' % example.id
    return "ajax('%s', [], '%s');" % (
                                        URL(r=request, f="example_part_edit.load", args=[example.id, field] ),
                                        target_id
                                        )
    
def example_part_view_4url():
    id, main_field, fields = analyse_args_about_fields()
    return example_part_view(id, main_field)
    
def example_part_view(id, main_field):
    empty_content_msg = T('...')
    example = db.Examples[id]

    if main_field == 'explanation':
        #~ content = XML(markdown(db.Topics[example.topic_id].explanation+'\n\n'+ example.explanation)) if example.explanation else '',
        plain_content = example.explanation or ''
        content = XML(markdown(plain_content)) if plain_content.strip() else empty_content_msg

    
    elif main_field == 'full_code':    
        #~ plain_content = '\n\n'.join( map(str, [parse_code_templates(example, 'pre_code') ,
                                               #~ example.code,
                                               #~ parse_code_templates(example, 'post_code')]
                        #~ ) ).strip()  
        # full_code = '\n\n'.join( example.pre_code , example.code, example.post_code) # this would not guarantee the newest info :/
        #~ content = XML( highlight_code(plain_content, db.Languages[example.lang_id]) ) \
                              #~ if plain_content.strip() else empty_content_msg
                              
        #~ or could be split in separate parts
        part_names = 'pre_code code post_code'.split()
        parts_txt = {
                  'pre_code': parse_code_templates(example, 'pre_code') ,
                  'code': example.code or '',
                  'post_code': parse_code_templates(example, 'post_code') ,
                }
                
        lang = db.Languages[example.lang_id]
        highlighted =  {}
        content = []
        classnames = {'code':'code'}
        classnames['pre_code'] = classnames['post_code'] = 'nonessential_surrounding_code'
        for part in part_names:
            txt = str(parts_txt[part])  # don't remember why, mut migth be issues with parse_code_templates
            if txt.strip(' \t\n\r'):  # if not empty
                highlighted[part] = XML(highlight_code(txt, lang, part))
            else :
                highlighted[part] = empty_content_msg if part=='code' else ''

            
            if classnames[part] == 'nonessential_surrounding_code':
                # apply toggling 
                _display = 'block' if classnames[part] in user_prefs.shown_example_parts else 'none'
            else:
                _display = ''

            content.append (
                            DIV( highlighted.get(part, '') ,
                            _class=classnames[part],
                            _style="display: " +  _display,
                            )
                        )
                                      
        #~ if highlighted : content = [ DIV( highlighted.get(part, '') , _class=classnames[part]) for part in part_names ]
        #~ else: content = empty_content_msg
        
        #~ pre_code = XML(highlight_code(pre_code, db.Languages[example.lang_id].name) ),
        #~ code = XML(highlight_code(code, db.Languages[example.lang_id].name) ),
        #~ post_code = XML(highlight_code(post_code, db.Languages[example.lang_id].name) ),
        
    elif main_field == 'output':
        plain_content = parse_code_templates(example, 'output') or example.topic_id.output or '' # force topic output if example output empty
        content = DIV( T("Output:"), PRE( plain_content )  )  if plain_content.strip() else empty_content_msg

    elif main_field == 'language_can_do_this':
        content = T("This language doesn't support this feature")
        main_field = 'explanation'  # a workaround, not to add weird extra field in Shown Languages
        
    else:
        content = T("ERROR: field \"%s\" doesn't have representation in 'example_part_view'") % main_field 
    return DIVify_2edit_dblcicked(example, main_field, content )



def example_delete_references(id):
    """to prevent brokent references after example deletion
    """
    for field in 'pre_code post_code output'.split():
        db(db.Examples[field+'_tpl_depends_on'].contains(id) ).update(  **{ field+'_tpl_depends_on':  db.Examples[field+'_tpl_depends_on'].difference(set([id])) } )
    db(db.Examples.tpl_invalidator == id ).update(  tpl_invalidator = None )
 
@auth.requires_permission('update', 'Examples')
def example_part_edit():
    id, main_field, fields = analyse_args_about_fields()
    example = db.Examples[id]
    #~ if request.function.endswith('.load'):
        #~ redirect_url = URL(r=request, f=example_view, args=id)
    #~ else:
        #~ redirect_url = request.env.http_referer
    #~ redirect_url = URL(r=request, f=example_view_4url, args=id)
    redirect_url = URL(r=request, f="example_part_view_4url", args=request.args) # args=[example.id, field] )

    # preparation for editing -- show how tpl are rendered -- which part comes from where
    #~ for cache_field in 'pre_code post_code output'.split( ):
    adapted_fields = []
    for field in fields:
        if field in 'pre_code post_code output'.split( ):
            db.Examples[field].writable = False

            current_field_value = example[field]
            parsed_field =  parse_code_templates(example, field )
            if parsed_field != current_field_value:
                example.update_record( **{ field: parsed_field } )
                cache_invalidate_4example(example.id)
            
            adapted_fields.append(field+'_tpl')
            adapted_fields.append(field)

            # append also monster parsing-debugging stuff 
            if field in 'post_code pre_code'.split(): 
                parsed_field =  parse_code_templates(example, field,
                                                inclusions_dbg_info=True,
                                                inclusions_dbg_levels_to_wrap=1, 
                                                callback4each_plain_line=lambda x: highlight_code(x, db.Languages[example.lang_id], field) ,
                                                )
                dbg_field = field + '_4parsing_dbg_info'
                adapted_fields.append(dbg_field)
                #~ db.Examples[dbg_field].writable = False # TODO: set in db model
            #~ print '== parsed_fields:', cache_field,  "\n", parsed_fields
            #~ db.Examples[cache_field].default = XML(highlight_code(parsed_field, db.Languages[example.lang_id].name) )
            #~ db.Examples[cache_field].represent = lambda x: parsed_fields[cache_field]
                try:
                    example.update_record( **{ dbg_field: parsed_field.decode('utf-8') } )
                    #~ example.update_record( **{ dbg_field: parsed_field } )
                    #~ example.update_record( **{ dbg_field: parsed_field.decode('utf-8') } )
                    #~ example.update_record( **{ dbg_field: XML( unicode(parsed_field.decode('utf-8')) ) } )
                    #~ example.update_record( **{ dbg_field: XML(parsed_field) } )
                except Exception, e:
                    mylog("example_part_edit dbg_field : ", type(e), str(e))
                    example.update_record( **{ dbg_field: str( ("ERROR example_part_edit dbg_field : ", type(e), str(e))) } )
                    
                    #~ example.update_record( **{ dbg_field: str(e) } )
                    #~ example.update_record( **{ dbg_field: latinize(parsed_field) } )
                # dbg fields are  nonessential for cache key reseting
                #~ cache_invalidate_4example(example.id)

        else: 
            adapted_fields.append(field)  # just don't forget other fields
        
            
    #~ db.Examples.changed_fields.requires = IS_IN_SET( [name+'_tpl' if not name.endswith('_tpl') else dname for name in  fields] )
    fields2monitor = [ field+'_tpl' if field in 'pre_code post_code output'.split() else field    for field in fields ]
    mylog ('fields2monitor', fields2monitor)
    #~ db.Examples.changed_fields.update = [] # TODO: check if it is possible to set it...
    #~ if len(fields2monitor) == 1:
        #~ db.Examples.changed_fields.update = fields2monitor
    
    db.Examples.changed_fields.requires = IS_IN_SET( fields2monitor, multiple=True )
    #~ fields_values_before_update = dict( [ (field, example[field]) for field in fields] )
    form=SQLFORM( db.Examples, example,
                  fields=adapted_fields + ['comment', 'language_can_do_this'], #db.Versioning.fields ,
                  _action=URL(r=request, args=request.args) ,
                  hidden=dict(_next=redirect_url),
                  
                )
    form.vars.comment = ''
    #~ import rpdb2; rpdb2.start_embedded_debugger('asdf')
    if form.accepts(request.vars, 
                    #~ ?? onaccept=crud.archive , ?? probably doesn't work, because it is in form.accepts clause
                    onvalidation=crud.archive ,
                    formname="example_%s_%s" % (main_field, id) 
        ):
            
        
        # set changed_fields if they are not set
        #~ if not form.vars['changed_fields']:  # if changed_fields are empty -- better force selecting changed_fields
        changed_fields = [f for f in fields2monitor if form.record[f] != form.vars[f] ]
        #~ changed_fields = []
        #~ for field in fields2monitor: 
            #~ if form.record[field] != form.vars[field]:  # compare previuos value with submitted in form
                #~ changed_fields.append( field )
        mylog ('changed_fields', changed_fields)
        db.Examples[example.id].update_record( changed_fields=changed_fields )
        #~ form.record.update_record( changed_fields=changed_fields )

        #~ crud.archive(form) # might be too late???
        
        #~ response.flash=T("Example (%s) fields \"%s\" updated.") % (id, fields) 

        example = db.Examples[id]

        # invalidation of dependants
        # using code_tpl_
        #~ for dependant_id in example.topic_id.tpl_influences:
            #~ db(db.Examples.topic_id==dependant_id)(db.Examples.lang_id==example.lang_id).update_record(tpl_valid=False)
        for field in 'pre_code post_code output'.split():
            # TODO: check which code_parts were changed from archives or changed_fields -- if it would track code part
            db(db.Examples[field+'_tpl_depends_on'].contains(id) ).update( **{field+'_tpl_valid': False, 'tpl_invalidator':id, 'tpl_invalidation_timestamp':request.now} )
            for dependent_example in db(  db.Examples[field+'_tpl_depends_on'].contains(id)  ).select():            
                cache_invalidate_4example(dependent_example.id)
            #~ example.update_record(tpl_valid=False, tpl_invalidator=id, tpl_invalidation_timestamp=request.now) #  problem with record self referencing http://groups.google.com/group/web2py/browse_thread/thread/0a61b01c9a4c7b02#
            example.update_record( **{field+'_tpl_valid': False, 'tpl_invalidator':None, 'tpl_invalidation_timestamp':request.now} )
            
        # TODO: check which code_parts were changed -- but for situation, when each code_part has its own id -- separate table for generated stuff
        #~ for dependent_example in db(  db.Examples['pre_code_tpl_depends_on'].contains(id) | db.Examples['post_code_tpl_depends_on'].contains(id) | db.Examples['output_tpl_depends_on'].contains(id) ).select():
            #~ response.flash += str( ( "caching invalidated by example", id, "in example", dependent_example.id ) )
            #~ cache_invalidate_4example(dependent_example.id)
        cache_invalidate_4example(example.id)

        #~ # using cache
        #~ cache.ram.clear(regex=cache_key_4example()) # clear itself
        #~ topic = db.Topics[example.topic_id]
        #~ cache.ram.clear(regex='.*?%(topic_path_name_part)s.*?%(lang_name)s$' %
                #~ dict(topic_path_name_part=topic_path_sep+example.topic_id.name+topic_path_sep,
                     #~ lang_name = example.lang_id.name
                    #~ )
                #~ ) # clear influenced ones (dependants)
        
        #~ return LOAD(request.http_referrer)
        if redirect_url: redirect(redirect_url)  # TODO: uncomment :)
        
    else:
        response.flash=T("Data not saved: %s" % (request.vars)), 
    #~ editareas = []
    #~ for field in fields():
        #~ editareas.append( TEXTAREA( value=example[field] , _name="example_%s_%s" % (id, field) ) )
    #~ return FORM ( *editareas, SUBMIT(), action='' )
    #~ return dict(form=form, http_referer=request.env.http_referer, http_origin=request.env.http_origin)
    return form


#~ def highlight_code_outside():
#~ 
    #~ url = "http://pygments-4cbe.appspot.com/"
    #~ ##~ url = "http://pygments.appspot.com/"
    #~ ##~ url = "http://localhost:8080/"
    #~ values = {'lang': 'python', 'code':
    #~ """
#~ for x in range(10):
  #~ print x"""
  #~ }
#~ 
    #~ import urllib
    #~ import urllib2
              #~ 
    #~ ##~ params = urllib.urlencode(values)
    #~ ##~ f = urllib.urlopen(url, params)
    #~ ##~ python_way = f.read()
#~ 
    #~ #http://www.voidspace.org.uk/python/articles/urllib2.shtml
    #~ data = urllib.urlencode(values)
    #~ req = urllib2.Request(url, data)
    #~ response = urllib2.urlopen(req)
    #~ python_way = the_page = response.read()
    #~ 
    #~ ajax_way = DIV(
    #~ DIV('Python', _id='lang'),
    #~ INPUT( value='print 4', _id='code'),
    #~ INPUT(  _value='go', _type='button', _onclick="ajax('http://pygments.appspot.com/', ['lang', 'code'], 'out'); " ), 
    #~ DIV( 'bla', _id='out'), 
    #~ );
    #~ return dict( ajax_way=ajax_way,  python_way=XML(python_way))
    

try:
    import pygments
    # http://github.com/trevorturk/pygments
    from pygments.lexers import get_lexer_by_name
    from pygments.formatters import HtmlFormatter        
    #~ pygments = local_import('pygments')
    #~ lexers = local_import('pygments.lexers')
    #~ formatters = local_import('pygments.formatters')
    
    pygments_imported = True
except Exception, e:
    mylog(msg=str(e))
    pygments_imported = False

def highlight_code(code, lang, code_part=None):
    if pygments_imported:
    # for serverside was
        try:
            lexer = get_lexer_by_name(lang.name.lower())
            #~ formatter = HtmlFormatter(noclasses=True, encoding='utf-8') #cssclass=filetype) # linenos=True,
            formatter = HtmlFormatter(noclasses=True, outencoding='utf-8') # , outencoding='utf-8' cssclass=filetype) # linenos=True,
            #~ formatter = HtmlFormatter(noclasses=True) # , outencoding='utf-8' cssclass=filetype) # linenos=True,
            # if we want to embed css via <style> into html
            style_defs = formatter.get_style_defs()

            code = code.decode('utf-8')
            #~ code = code.encode('utf-8')
            #~ code = code.encode()
            #~ code = unicode(code, 'utf-8')
            highlighted_code = pygments.highlight(code, lexer, formatter)
            return unicode(highlighted_code, 'utf-8')
            #~ return highlighted_code
            
        except Exception, e:
            mylog( "ERROR in highlight_code: ", msg=str(e),
                args=(lang.name, code_part, str(code) )
                )
            pass
            
    # if no luck with pygments
    # let's use clienside: javascript 
    # http://www.webdesignbooth.com/9-useful-javascript-syntax-highlighting-scripts/

    # http://alexgorbatchev.com/SyntaxHighlighter/manual/installation.html
    if code_part == 'code':
        pass
    return PRE(  code, _class="brush: %s" % lang.extension )
    
    #~ #  http://google-code-prettify.googlecode.com/svn/trunk/README.html
    #~ return PRE(  code, _class="prettyprint lang-%s" % lang.extension )



# Example Update's
@auth.requires_permission('update', 'Examples')
def example_update_form(example, author=None):
    db.Examples.topic_id.writable = False
    db.Examples.lang_id.writable = False
    db.Examples.timestamp.default=request.now
    if example_is_empty(example.id):  # don't forget the author...
        try:
            db.Examples.author_id.default=auth.user.id
        except AttributeError:
            response.flash = T("You should be logged in to edit") 
    #db.Examples.author_id.default=auth.user.id
    redirect_url = URL(r=request,f='example_view_4url.load',args=[example.id])
    form=crud.update(db.Examples, example, next=redirect_url, onaccept=crud.archive, deletable = False)
    # onaccept=lambda form: crud.archive(form) if (cache_invalidate_4example(example.id) or True) # a hack to change onaccept
    if form.accepts( request.vars, session ):
        cache_invalidate_4example(example.id)
        redirect( redirect_url )
    return form
    #return dict(form=form)

def example_update():
    try:    id = int( request.args[0])
    except:
        return "Problem, wrong id: %s" % request.args
    form = example_update_form( db.Examples[id] )
    return form
    #return dict(form=form)

def ajax_example_update():
    return example_update().xml()

 

# Example Create's -- might become deprecated (if we will use workaround for creating)
@auth.requires_permission('update', 'Examples')
def example_create_form(topic_id, lang_id=None):
    db.Examples.topic_id.default = topic_id
    db.Examples.topic_id.writable = False
    db.Examples.timestamp.default=request.now
    try:
        db.Examples.author_id.default=auth.user.id
    except AttributeError:
        pass
    if lang_id:
        db.Examples.lang_id.default = lang_id
        db.Examples.lang_id.writable = False
        
    form=crud.create(db.Examples)
    return form

def example_changelog():
    id = int( request.args(0) )
    #~ result = ["example_changelog/%s: " % id]
    result = []
    #~ db.Examples_archive.author_id.represent= lambda id: db.auth_user[id].first_name
    #~ db.Examples_archive.author_id.represent= lambda id: db.auth_user[id].first_name
    for archived_example in db(db.Examples_archive.current_record == id).select(orderby=~db.Examples_archive.id):
         result.append(
                TOGGLE( stuff=example_archive_view_full(archived_example)  , stuff_id="example_archive_view_%s"% archived_example.id,
                         msg=example_archive_view_log(archived_example)
                )
         )
    return UL(result)
    #~ return "example_logs %s :)" % id

def example_archive_view_log(archived_example):
    return DIV(
        example_archive_view_fields(archived_example, 'comment changed_fields'.split() , represent = "%(val)s" ),
        #~ BR(),
        example_archive_view_fields(archived_example, 'author_id timestamp'.   split() , represent = "%(val)s" )
        )
    
def example_archive_view_full(archived_example):
    #~ return example_archive_view_fields(archived_example, 'explanation pre_code code post_code output'.split(), result_wrapper_TAG=UL )
    return BEAUTIFY(dict( [(field, archived_example[field]) for field in  'explanation pre_code code post_code output'.split() ] ) )
    
def example_archive_view_fields(
                    archived_example,
                    fields=['id'],
                    field_wrapper_TAG=SPAN,
                    result_wrapper_TAG=DIV,
                    separator = ' ',
                    represent = "%(field)s: %(val)s",
                    
                    ):
    result = []
    for field in fields:
        result.append( field_wrapper_TAG(represent % dict(
                        field=field,
                        val=db.Examples_archive[field].represent( archived_example[field]) if db.Examples_archive[field].represent else  archived_example[field]
                    ) ) )
        result.append( separator )
    return result_wrapper_TAG(result)

    

def example_create(): #for experimenting
    try:    topic_id = request.args[0]
    except: topic_id = 1
    try:    lang_id = request.args[1]
    except: lang_id = None
    form = example_create_form(topic_id, lang_id) # crud.create(db.Examples)
    return form
    #return dict(form=form)

def ajax_example_create():
    return example_create().xml()
# -----------------


# deprecated -- more convenient to pass arguments via URL not via vars...
def ajax_example_create_form():
    errors = []
    try: lang_id = int(request.vars.lang_id)
    except Exception, e: lang_id = None #errors.append("Bad language id: %s"% str(request.vars.lang_id)) 
    try: topic_id = int(request.vars.topic_id)
    except Exception, e: errors.append("Bad topic id. request.args = %s"% str(request.vars.topic_id)) 
    if errors: 
        return errors  #dict(erros=errors)
    return example_create_form(topic_id, lang_id).xml()

